/*
 * XmlMapLoader.java
 *
 * Created on 3. Februar 2007, 13:54
 *
 * Copyright (c) 2007 Daniel Gronau
 *
 * This file is part of KanjiTori.
 *
 * KanjiTori is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * KanjiTori is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 */

package kanjitori.map;

import com.sun.org.apache.xerces.internal.parsers.SAXParser;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import kanjitori.abstraction.Factory;
import kanjitori.map.RenderStateHolder;
import kanjitori.map.tile.Tile;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

/**
 *
 * @author Pirx
 */
public class XmlMapLoader implements Factory<Map> {

    /** Creates a new instance of XmlMapLoader */
    public XmlMapLoader() {
    }

    /**
     * 
     * @param mapFileName 
     * @return 
     */
    public Map get(String mapFileName) {
        try {
            DefaultMap map = new DefaultMap();
            XMLReader xmlReader = new SAXParser();
            xmlReader.setContentHandler(new MapHandler(map));
            InputSource is = new InputSource(new FileInputStream(mapFileName));
            xmlReader.parse(is);
            return map;
        } catch(Exception ex) {
            throw new IllegalArgumentException(ex);
        }
    }

    private static class MapHandler  extends DefaultHandler {
    
        private final String NAME = "name";
        
        private final String MAP = "map";
        private final String MAP_START_BOTS = "startBots";
        private final String MAP_X = "x";
        private final String MAP_Y = "y";

        private final String RENDERSTATE = "renderstate";
        
        private final String TILE = "tile";
        private final String TILE_CLASS = "class";
        private final String TILE_CHAR = "char";
        
        private final String PARAM = "param";
        private final String PARAM_KEY = "key";
        private final String PARAM_VALUE = "value";
        
        private final String LAYERS = "layers";
        private final String LAYER = "layer";
        private final String LAYER_HEIGHT = "height";
        
        private final String CONTENT = "content";
        
        private final String SKYBOX = "skybox";
        private final String SKYBOX_UP = "up";
        private final String SKYBOX_DOWN = "down";
        private final String SKYBOX_NORTH = "north";
        private final String SKYBOX_SOUTH = "south";
        private final String SKYBOX_EAST = "east";
        private final String SKYBOX_WEST = "west";
        
        private final String LINE = "l";
        private final String LINE_STRING = "s";
        
        private final char SPACE = ' ';
    
        private DefaultMap map;
        private List<Layer> layers = new ArrayList<Layer>();
        private Layer layer;
        private List<String> lines = new ArrayList<String>();

        private Tile tile;
        private RenderStateHolder renderstate;
        private java.util.Map<String, String> params = new HashMap<String, String>();

        MapHandler(DefaultMap map) {
            this.map = map;
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes){
            if (MAP.equals(qName)) {
                map.setName(attributes.getValue(NAME));
                map.setBotCount(Integer.parseInt(attributes.getValue(MAP_START_BOTS)));
                map.setSize(Position.get(Integer.parseInt(attributes.getValue(MAP_X)),
                        Integer.parseInt(attributes.getValue(MAP_Y))));
            } else if (RENDERSTATE.equals(qName)) {
                renderstate = new RenderStateHolder(attributes.getValue(NAME));
            } else if (TILE.equals(qName)) {
                try {
                    tile = (Tile) Class.forName(attributes.getValue(TILE_CLASS)).newInstance();
                    tile.setName(attributes.getValue(NAME));
                    tile.setChar(attributes.getValue(TILE_CHAR).charAt(0));
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            } else if (PARAM.equals(qName)) {
                params.put(attributes.getValue(PARAM_KEY), attributes.getValue(PARAM_VALUE));
            } else if(LAYER.equals(qName)) {
                layer = new DefaultLayer(attributes.getValue(NAME), map.getSize(),
                        Float.parseFloat(attributes.getValue(LAYER_HEIGHT)));
            } else if (CONTENT.equals(qName)) {
                map.setContentMatrix(new DefaultContentMatrix(map.getSize()));
            } else if (SKYBOX.equals(qName)) {
                map.setSkyboxTextures(attributes.getValue(SKYBOX_NORTH),
                                   attributes.getValue(SKYBOX_SOUTH),
                                   attributes.getValue(SKYBOX_EAST),
                                   attributes.getValue(SKYBOX_WEST),
                                   attributes.getValue(SKYBOX_UP),
                                   attributes.getValue(SKYBOX_DOWN));
            } else if (LINE.equals(qName)) {
                lines.add(attributes.getValue(LINE_STRING));
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName) {
            if (LAYERS.equals(qName)) {
                map.setLayers(layers.toArray(new Layer[layers.size()]));
            } else if (TILE.equals(qName)) {
                tile.setParams(params);
                map.addTile(tile);
                params = new HashMap<String, String>();
            } else if (RENDERSTATE.equals(qName)) {
                renderstate.setParams(params);
                map.addRenderStateHolder(renderstate);
                params = new HashMap<String, String>();
            } else if (LAYER.equals(qName)) {
                for (int y = map.getSize().y()-1; y >= 0; y--) {
                    String string = lines.get(y);
                    for (int x = 0; x < map.getSize().x(); x++) {
                        Tile tile = map.getTile(string.charAt(x));
                        layer.setTile(Position.get(x, y), tile);
                    }
                }
                lines.clear();
                layers.add(layer);
           } else if (CONTENT.equals(qName)) {
                for (int y = map.getSize().y() - 1; y >= 0; y--) {
                    String string = lines.get(y);
                    for (int x = 0; x < map.getSize().x(); x++) {
                         map.getContentMatrix().setContent(Position.get(x, y),
                         string.charAt(x) == SPACE ? Content.EMPTY : Content.SOLID);
                    }
                }
                lines.clear();
           } 
        }
    }

}